/**
 * Simple HTTP Server Manager
 * Provides persistent HTTP configuration service that runs alongside MCP
 * Uses only Node.js built-in modules following KISS principle
 */

const http = require('http');
const url = require('url');
const fs = require('fs');
const path = require('path');
const { detectEnvironment, PortChecker } = require('../utils/environment');
const ConfigManager = require('../config/config-manager');
const { ErrorFactory, ErrorHandler } = require('../utils/errors');
const { getLogger } = require('../logging/logger');

const logger = getLogger();

/**
 * Simple HTTP Server Manager class
 */
class HttpServerManager {
  /**
   * Create a new HTTP server manager
   * @param {Object} config - Configuration options
   */
  constructor(config = {}) {
    this.config = {
      port: 3000,
      host: 'localhost',
      auto_start: true,
      cors: {
        enabled: true,
        origins: ['*']
      },
      ...config
    };

    // Set direct properties for easier access
    this.port = this.config.port;
    this.host = this.config.host;

    this.server = null;
    this.isRunning = false;
    this.startTime = null;
    this.configManager = new ConfigManager();
    this.routes = new Map();

    this._setupDefaultRoutes();
  }

  /**
   * Setup default routes
   * @private
   */
  _setupDefaultRoutes() {
    // Health check endpoint
    this.addRoute('GET', '/health', (req, res) => {
      this._sendJson(res, 200, {
        status: 'ok',
        timestamp: new Date().toISOString(),
        uptime: this.isRunning ? Date.now() - this.startTime.getTime() : 0,
        version: this._getVersion()
      });
    });

    // Server status endpoint
    this.addRoute('GET', '/status', (req, res) => {
      this._sendJson(res, 200, this.getServerStatus());
    });

    // Root endpoint
    this.addRoute('GET', '/', (req, res) => {
      this._sendHtml(res, 200, this._getIndexHtml());
    });

    // Static file serving for basic web interface
    this.addRoute('GET', '/public/*', (req, res) => {
      const filePath = req.url.replace('/public', '');
      this._serveStaticFile(res, filePath);
    });
  }

  /**
   * Add a new route
   * @param {string} method - HTTP method
   * @param {string} path - Route path
   * @param {Function} handler - Route handler
   */
  addRoute(method, path, handler) {
    const key = `${method.toUpperCase()} ${path}`;
    this.routes.set(key, handler);
  }

  /**
   * Create HTTP request handler
   * @private
   */
  _createRequestHandler() {
    return async (req, res) => {
      try {
        // Parse URL
        const parsedUrl = url.parse(req.url, true);
        req.pathname = parsedUrl.pathname;
        req.query = parsedUrl.query;

        // Add CORS headers if enabled
        if (this.config.cors.enabled) {
          this._addCorsHeaders(res, req.headers.origin);
        }

        // Handle OPTIONS requests for CORS
        if (req.method === 'OPTIONS') {
          res.writeHead(200);
          res.end();
          return;
        }

        // Log request
        logger.debug('HTTP request', {
          method: req.method,
          url: req.url,
          ip: req.socket.remoteAddress,
          userAgent: req.headers['user-agent']
        });

        // Parse body for POST/PUT requests
        if (req.method === 'POST' || req.method === 'PUT') {
          req.body = await this._parseRequestBody(req);
        }

        // Find matching route
        const handler = this._findRoute(req.method, req.pathname);
        if (handler) {
          await handler(req, res);
        } else {
          this._sendJson(res, 404, {
            error: 'Not Found',
            message: `Route ${req.method} ${req.pathname} not found`
          });
        }

      } catch (error) {
        logger.error('HTTP server error', {
          error: error.message,
          stack: error.stack,
          url: req.url,
          method: req.method
        });

        this._sendJson(res, 500, {
          error: 'Internal Server Error',
          message: process.env.NODE_ENV === 'development' ? error.message : 'Something went wrong'
        });
      }
    };
  }

  /**
   * Find matching route for request
   * @private
   */
  _findRoute(method, pathname) {
    const exactKey = `${method.toUpperCase()} ${pathname}`;
    if (this.routes.has(exactKey)) {
      return this.routes.get(exactKey);
    }

    // Try pattern matching
    for (const [key, handler] of this.routes) {
      const [routeMethod, routePath] = key.split(' ');
      if (routeMethod !== method.toUpperCase()) continue;

      if (this._matchesPath(routePath, pathname)) {
        return handler;
      }
    }

    return null;
  }

  /**
   * Check if route path matches request pathname
   * @private
   */
  _matchesPath(routePath, pathname) {
    if (routePath === pathname) return true;

    // Handle wildcard routes
    if (routePath.endsWith('*')) {
      const prefix = routePath.slice(0, -1);
      return pathname.startsWith(prefix);
    }

    // Handle parameterized routes
    const routeParts = routePath.split('/').filter(Boolean);
    const pathParts = pathname.split('/').filter(Boolean);

    if (routeParts.length !== pathParts.length) return false;

    return routeParts.every((part, index) =>
      part.startsWith(':') || part === pathParts[index]
    );
  }

  /**
   * Parse request body
   * @private
   */
  async _parseRequestBody(req) {
    return new Promise((resolve, reject) => {
      let body = '';

      req.on('data', chunk => {
        body += chunk.toString();
      });

      req.on('end', () => {
        try {
          // Try to parse as JSON first
          if (req.headers['content-type']?.includes('application/json')) {
            resolve(JSON.parse(body || '{}'));
          } else {
            // Fall back to plain text
            resolve(body);
          }
        } catch (error) {
          reject(error);
        }
      });

      req.on('error', reject);
    });
  }

  /**
   * Add CORS headers
   * @private
   */
  _addCorsHeaders(res, origin) {
    const allowedOrigins = this.config.cors.origins;

    if (allowedOrigins.includes('*') || allowedOrigins.includes(origin)) {
      res.setHeader('Access-Control-Allow-Origin', origin || '*');
      res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
      res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
    }
  }

  /**
   * Send JSON response
   * @private
   */
  _sendJson(res, statusCode, data) {
    res.writeHead(statusCode, {
      'Content-Type': 'application/json',
      'Access-Control-Allow-Origin': '*'
    });
    res.end(JSON.stringify(data, null, 2));
  }

  /**
   * Send HTML response
   * @private
   */
  _sendHtml(res, statusCode, html) {
    res.writeHead(statusCode, {
      'Content-Type': 'text/html; charset=utf-8',
      'Access-Control-Allow-Origin': '*'
    });
    res.end(html);
  }

  /**
   * Serve static file
   * @private
   */
  _serveStaticFile(res, filePath) {
    try {
      const fullPath = path.join(__dirname, '../../public', filePath);

      if (!fs.existsSync(fullPath)) {
        this._sendJson(res, 404, {
          error: 'File Not Found',
          message: `File ${filePath} not found`
        });
        return;
      }

      const content = fs.readFileSync(fullPath);
      const ext = path.extname(fullPath).toLowerCase();
      const contentType = this._getContentType(ext);

      res.writeHead(200, {
        'Content-Type': contentType,
        'Access-Control-Allow-Origin': '*'
      });
      res.end(content);

    } catch (error) {
      logger.error('Failed to serve static file', {
        filePath,
        error: error.message
      });

      this._sendJson(res, 500, {
        error: 'Internal Server Error',
        message: 'Failed to serve file'
      });
    }
  }

  /**
   * Get content type for file extension
   * @private
   */
  _getContentType(ext) {
    const types = {
      '.html': 'text/html; charset=utf-8',
      '.css': 'text/css; charset=utf-8',
      '.js': 'application/javascript; charset=utf-8',
      '.json': 'application/json',
      '.png': 'image/png',
      '.jpg': 'image/jpeg',
      '.jpeg': 'image/jpeg',
      '.gif': 'image/gif',
      '.svg': 'image/svg+xml'
    };

    return types[ext] || 'application/octet-stream';
  }

  /**
   * Get package version
   * @private
   */
  _getVersion() {
    try {
      const packagePath = path.join(__dirname, '../../package.json');
      const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
      return packageJson.version;
    } catch (error) {
      return '1.0.0';
    }
  }

  /**
   * Get index HTML
   * @private
   */
  _getIndexHtml() {
    return `<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vision Analyzer - Configuration</title>
    <style>
      body {
        font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
        margin: 0;
        padding: 20px;
        background-color: #f5f5f5;
      }
      .container {
        max-width: 800px;
        margin: 0 auto;
        background: white;
        padding: 20px;
        border-radius: 8px;
        box-shadow: 0 2px 4px rgba(0,0,0,0.1);
      }
      h1 {
        color: #333;
        margin-bottom: 20px;
      }
      .status {
        padding: 10px;
        border-radius: 4px;
        margin-bottom: 20px;
      }
      .status.running {
        background-color: #d4edda;
        color: #155724;
        border: 1px solid #c3e6cb;
      }
      .status.stopped {
        background-color: #f8d7da;
        color: #721c24;
        border: 1px solid #f5c6cb;
      }
    </style>
</head>
<body>
    <div class="container">
        <h1>Vision Analyzer</h1>
        <div id="status" class="status">
            Loading status...
        </div>
        <div id="content">
            <h2>Configuration Interface</h2>
            <p>This is a basic configuration interface for Vision Analyzer.</p>
            <p>Server is running in simplified mode without Express.</p>
        </div>
    </div>
    <script>
        // Simple JavaScript for status updates
        async function updateStatus() {
            try {
                const response = await fetch('/status');
                const status = await response.json();
                const statusDiv = document.getElementById('status');
                statusDiv.className = status.running ? 'status running' : 'status stopped';
                statusDiv.textContent = status.running
                    ? \`Server is running on port \${status.port}\`
                    : 'Server is stopped';
            } catch (error) {
                document.getElementById('status').className = 'status stopped';
                document.getElementById('status').textContent = 'Failed to get server status';
            }
        }

        updateStatus();
        setInterval(updateStatus, 5000);
    </script>
</body>
</html>`;
  }

  /**
   * Start the HTTP server
   * @returns {Promise<Object>} Server start result
   */
  async startServer() {
    try {
      if (this.isRunning) {
        return {
          success: false,
          error: 'Server is already running',
          port: this.port,
          host: this.host
        };
      }

      // Load configuration if not provided
      await this._loadConfiguration();

      // Check if port is available
      const portAvailable = await this.isPortAvailable(this.port, this.host);
      if (!portAvailable) {
        logger.warn(`Port ${this.port} is not available, looking for alternative`);
        const availablePort = await PortChecker.findAvailablePort(
          [this.port, ...this._getFallbackPorts()],
          this.host
        );

        if (!availablePort) {
          throw ErrorFactory.network('No available ports found', `${this.host}:${this.port}`);
        }

        this.port = availablePort;
        logger.info(`Using alternative port: ${this.port}`);
      }

      // Create and start server
      this.server = http.createServer(this._createRequestHandler());

      return new Promise((resolve, reject) => {
        this.server.listen(this.port, this.host, async (error) => {
          if (error) {
            logger.error('Failed to start HTTP server', {
              port: this.port,
              host: this.host,
              error: error.message
            });

            reject({
              success: false,
              error: error.message,
              port: this.port,
              host: this.host
            });
            return;
          }

          this.isRunning = true;
          this.startTime = new Date();

          logger.info('HTTP server started successfully', {
            port: this.port,
            host: this.host,
            address: this.server.address()
          });

          resolve({
            success: true,
            port: this.port,
            host: this.host,
            address: this.server.address()
          });
        });

        // Handle server errors
        this.server.on('error', (error) => {
          logger.error('HTTP server error', {
            error: error.message,
            code: error.code
          });

          if (error.code === 'EADDRINUSE') {
            reject({
              success: false,
              error: `Port ${this.port} is already in use`,
              port: this.port,
              host: this.host
            });
          } else {
            reject({
              success: false,
              error: error.message,
              port: this.port,
              host: this.host
            });
          }
        });
      });

    } catch (error) {
      const handledError = ErrorHandler.handle(error, {
        operation: 'start_http_server',
        port: this.port,
        host: this.host
      });

      return {
        success: false,
        error: handledError.message,
        port: this.port,
        host: this.host
      };
    }
  }

  /**
   * Stop the HTTP server (simplified)
   * @returns {Promise<Object>} Server stop result
   */
  async stopServer() {
    if (!this.isRunning || !this.server) {
      return {
        success: true,
        message: 'Server is not running'
      };
    }

    try {
      this.server.close();
      this.isRunning = false;
      this.startTime = null;

      logger.info('HTTP server stopped');
      return {
        success: true,
        message: 'Server stopped'
      };
    } catch (error) {
      logger.error('Failed to stop HTTP server', error.message);
      return {
        success: false,
        error: error.message
      };
    }
  }

  /**
   * Restart the HTTP server
   * @returns {Promise<Object>} Restart result
   */
  async restartServer() {
    try {
      logger.info('Restarting HTTP server');

      // Stop current server
      const stopResult = await this.stopServer();
      if (!stopResult.success) {
        return stopResult;
      }

      // Wait a moment before starting
      await new Promise(resolve => setTimeout(resolve, 100));

      // Start server again
      const startResult = await this.startServer();

      logger.info('HTTP server restarted successfully', {
        success: startResult.success,
        port: startResult.port
      });

      return startResult;

    } catch (error) {
      const handledError = ErrorHandler.handle(error, {
        operation: 'restart_http_server'
      });

      return {
        success: false,
        error: handledError.message
      };
    }
  }

  /**
   * Get current server status
   * @returns {Object} Server status
   */
  getServerStatus() {
    const uptime = this.isRunning && this.startTime
      ? Date.now() - this.startTime.getTime()
      : 0;

    return {
      running: this.isRunning,
      port: this.port,
      host: this.host,
      uptime,
      startTime: this.startTime,
      address: this.server ? this.server.address() : null,
      config: {
        cors: this.config.cors,
        auto_start: this.config.auto_start
      }
    };
  }

  /**
   * Check if a port is available
   * @param {number} port - Port to check
   * @param {string} host - Host to bind to
   * @returns {Promise<boolean>} Whether port is available
   */
  async isPortAvailable(port, host = 'localhost') {
    try {
      return await PortChecker.isPortAvailable(port, host);
    } catch (error) {
      logger.warn('Failed to check port availability', {
        port,
        host,
        error: error.message
      });
      return false;
    }
  }

  /**
   * Update server configuration
   * @param {Object} newConfig - New configuration
   */
  updateConfiguration(newConfig) {
    try {
      // Validate configuration values
      if (newConfig.port && typeof newConfig.port === 'number') {
        if (newConfig.port < 1024 || newConfig.port > 65535) {
          logger.warn('Invalid port number, keeping current value', {
            newPort: newConfig.port,
            currentPort: this.port
          });
          return;
        }
        this.port = newConfig.port;
        this.config.port = newConfig.port;
      }

      if (newConfig.host && typeof newConfig.host === 'string') {
        this.host = newConfig.host.trim();
        this.config.host = this.host;
      }

      if (typeof newConfig.auto_start === 'boolean') {
        this.config.auto_start = newConfig.auto_start;
      }

      if (newConfig.cors && typeof newConfig.cors === 'object') {
        this.config.cors = { ...this.config.cors, ...newConfig.cors };
      }

      logger.info('Server configuration updated', {
        port: this.port,
        host: this.host,
        auto_start: this.config.auto_start
      });

    } catch (error) {
      logger.error('Failed to update server configuration', {
        error: error.message,
        config: newConfig
      });
    }
  }

  /**
   * Load configuration from config manager
   * @private
   */
  async _loadConfiguration() {
    try {
      const config = await this.configManager.getConfig();

      if (config && config.http_server) {
        this.updateConfiguration(config.http_server);
      }

    } catch (error) {
      logger.warn('Failed to load configuration, using defaults', {
        error: error.message
      });
    }
  }

  /**
   * Get fallback ports list
   * @private
   * @returns {number[]} Array of fallback ports
   */
  _getFallbackPorts() {
    const basePort = this.port;
    const fallbacks = [];

    // Generate 10 fallback ports starting from basePort + 1
    for (let i = 1; i <= 10; i++) {
      const port = basePort + i;
      if (port <= 65535) {
        fallbacks.push(port);
      }
    }

    // Add common alternative ports
    fallbacks.push(8080, 8081, 8888, 9000, 9001);

    return fallbacks;
  }

  /**
   * Get HTTP server instance
   * @returns {Object} HTTP server
   */
  getHttpServer() {
    return this.server;
  }
}

module.exports = HttpServerManager;